home *** CD-ROM | disk | FTP | other *** search
/ NetNews Offline 2 / NetNews Offline Volume 2.iso / news / comp / std / c++ / 558 < prev    next >
Encoding:
Internet Message Format  |  1996-08-06  |  10.4 KB

  1. From: fjh@munta.cs.mu.OZ.AU (Fergus Henderson)
  2. Message-ID: <4gu414$62u@mulga.cs.mu.OZ.AU>
  3. X-Original-Date: 27 Feb 1996 05:15:16 GMT
  4. Path: in1.uu.net!bounce-back
  5. Date: 27 Feb 96 07:29:29 GMT
  6. Approved: fjh@cs.mu.oz.au
  7. Newsgroups: comp.std.c++
  8. Subject: Re: operators new[]/delete[]
  9. Organization: Comp Sci, University of Melbourne
  10. References: <313166CF.11C2@orbotech.co.il>
  11. X-Auth: PGPMoose V1.1 PGP comp.std.c++
  12.     iQBFAgUBMTKy6OEDnX0m9pzZAQEo1AF/Q5+at62o6RsaOD2qkQtPJ4DaGReJKRe5
  13.     sM7uha7CwyYuFBTzxJuuQkXS8J4V5mG1
  14.     =igrN
  15.  
  16. "Constantine Antonovich:" <const@Orbotech.Co.IL> writes:
  17.  
  18. >              2. Undefined behavior.
  19. >
  20. >    Of  course, some  constructions  in   a possible program   may
  21. >produce undefined behavior.  Nevertheless, even undefined behavior
  22. >should have some definition.
  23.  
  24. I disagree.  Imposing restrictions on the behaviour of code
  25. which has "undefined behaviour" would be a very confusing use
  26. of terminology, and more importantly would place constraints on
  27. implementors that would prevent efficient implementations.
  28.  
  29. You may perhaps be able to make a case that certain specific cases
  30. of undefined behaviour ought to be instead made merely unspecified.
  31. But in the general case, a write via a stray pointer might cause
  32. arbitrary instructions to be executed, thus violating any guarantees.
  33. There is basically no way that an implementation which allows writes
  34. via stray pointers can prevent this.  Preventing stray pointer writes
  35. is possible, but would have a significant efficiency penalty.
  36.  
  37. >Let's consider the following code:
  38. >
  39. >//----------------------------------------------------------
  40. >      A* ap=new A;
  41. >      B* bp=reinterpret_cast<B*>(ap);
  42. >      delete bp; // #here
  43. >//----------------------------------------------------------
  44. >
  45. >Without any doubt, result of the code  executed in "#here" line is
  46. >undefined.   But  definitely  I  wouldn't   like,  as a result  of
  47. >uncertainty of the behavior, my  compiler to send email  complaint
  48. >to  some   League "C++  compilers     against  stupidity of    the
  49. >programmers".
  50.  
  51. You might not like it, but depending on the types `A' and `B',
  52. this could cause heap corruption on some implementations.
  53. For example, the compiler might represent pointers to `A' as
  54. pointing not directly to the start of the memory for `A', but
  55. instead pointing to some fixed offset before or after the start.
  56. The reinterpret_case<B*> might not adjust for that offset.
  57.  
  58. Heap corruption could cause writes through stray pointers,
  59. which could cause arbitrary code to be executed.  The result
  60. of that could be anything -- sending email complaints is
  61. unlikely, but can't be ruled out.
  62.  
  63. >Also   I  wouldn't like   my  compiler to recognize
  64. >incorrectness of the code and silently to call A destructor (after
  65. >all, bp  points to A  object, isn't it?) instead of  B one. Here I
  66. >mean that
  67. >
  68. >         UNDEFINED BEHAVIOR HAS AN ERROR MEANING.
  69. >   UNDEFINED BEHAVIOR ALWAYS RESULTING IN CORRECT EXECUTION OF
  70. >       A CODE WITH UNDEFINED BEHAVIOR, IS FORBIDDEN.
  71.  
  72. Preventing implementations from doing "the right thing" when
  73. executing code with undefined behaviour would place unreasonable
  74. constraints on implementors that would prevent efficient implementations.
  75.  
  76. For example, a write via a stray pointer might write to some ununsed
  77. memory, in which case it will have no effect, and the code may continue
  78. to work.  There is basically no way the implementation can avoid this
  79. other than by checking for stray pointer writes, which as I said before
  80. would have a significant efficient impact.
  81.  
  82. >           4. Alignment and memory allocation.
  83. >
  84. >    In the starting the article example, the following code
  85. >
  86. >//----------------------------------------------------------
  87. >    assert(sizeof(A)==sizeof(B));
  88. >
  89. >    B* bptr=new B[size];
  90. >    A* aptr=(A*)bptr;
  91. >//----------------------------------------------------------
  92. >
  93. >really seems dangerous.
  94. >
  95. >    Fergus Henderson writes:
  96. >       "This assertion is not guaranteed to succeed.
  97. >        It would take an extremely perverse implementation
  98. >        for it to fail, however, so I think it would be very
  99. >        portable, even though it is not strictly guaranteed
  100. >        to work."
  101. >
  102. >This sentence seems to be  reasonable but, in deal, this assertion
  103. >guarantees the  correctness     ALMOST ALWAYS  and    under   that
  104. >circumstance this check is absolutely portable.
  105.  
  106. I don't understand what you are saying here.  (This assertion guarantees
  107. the correctness of what?  Under which circumstances?)
  108.  
  109. >    An  implementation    hasn't  to be    extremely perverse  the
  110. >assertion condition  to fail.  It  can  be very simple  one where B
  111. >class for example has its own operator  new[] allocating memory in
  112. >specific alignment suitable for B but  not for any other class and
  113. >A  one  particularly (and  even  that  is impossible for compilers
  114. >still not supporting overloading of operator new[]).
  115.  
  116. In the test case, A and B were both identical classes (other than the
  117. class name); I think only a peverse implementation would allocate them
  118. different sizes.
  119.  
  120. Your talk about B having `operator new[]' is describing a hypothetical
  121. peice of source code, not a hypothetical C++ implementation; I don't
  122. see how it is relevant.
  123.  
  124. >    Fergus Henderson continues:
  125. >//----------------------------------------------------------
  126. >    B* bptr=new B[size];
  127. >    A* aptr=(A*)bptr;
  128. >//----------------------------------------------------------
  129. >        "This cast has unspecified behavior. (See 5.2.9
  130. >         [expr.cast.reinterpret]/8.). However, I would
  131. >         expect it to work on most implementations."
  132. >
  133. >    This  article    of ANSI draft    interprets the  operation as
  134. >unspecified in case of cast from T1 to T2 and back and if there is
  135. >difference in alignment of T1 and  T2.  Obviously, that is not our
  136. >case (at least because definition of allocation function returning
  137. >suitable for any object alignment).
  138.  
  139. That's irrelevant, since in your example piece of code, you don't cast
  140. back to `B *'.  5.2.9/8 says that the result in this case is unspecified.
  141.  
  142. >    Fergus Henderson agrees with Steve Clamage:
  143. >        "This has undefined behaviour.      It contravenes 5.3.5
  144. >         [expr.delete]/2,   which   says  that  the   expression
  145. >         passed  to `delete []'  must be  a pointer to the first
  146. >         element of an array of objects allocated with `new []';
  147. >         this is not the case,  because  although there once was
  148. >         such an  array at  that memory location,   its lifetime
  149. >         ended  when the memory  was  overwritten  by  the calls
  150. >         to placement new (see 3.8[basic.life]/1)."
  151. >
  152. >    There  is    at least one   self-contradictory  point  in that
  153. >conclusion.  Of course, lifetime of all B  objects had been ended,
  154. >by why does that  mean end of the  array life?
  155.  
  156. The lifetime of an array object is distinct from the lifetime of its
  157. elements.  The ending of the lifetime of all the B objects (which you
  158. did by explciitly calling the destructur) doesn't end the lifetime of
  159. the array.
  160.  
  161. What ends the lifetime of the array is reusing the memory (which
  162. you did by calling placement new).  3.8 is quite clear about this:
  163. "the lifetime of an array object ... ends when the storage which the array ...
  164. occupies is reused or released."
  165.  
  166. >Or in contrary,  if
  167. >end of life-time of B objects means end of life-time of the array,
  168. >so  creation  of  A objects  should  mean  creation of new  array,
  169. >shouldn't it?.
  170.  
  171. Yes.  But this array was not "created with new []" as required by
  172. 5.3.5.2, which you quite below, and thus the behaviour is still
  173. undefined.
  174.  
  175. >    Article 5.3.5.2 of ANSI draft says something slightly
  176. >    different:
  177. >        "...In the second  alternative  (delete array), the value
  178. >        of  the  operand  of  delete  shall  be  a pointer  to an
  179. >        array created by a new-expression without a new-placement
  180. >        specification. If not, the behavior is undefined."
  181. >
  182. >
  183. >    So delete takes as its argument POINTER TO ARRAY (even objects
  184. >are not mentioned).
  185.  
  186. This is indeed a minor error; it has been changed in the January 96
  187. draft to say "... shall be a pointer to the first element of an array ...".
  188.  
  189. >No one says that 
  190. >        ...pointer passed to delete[] must match the type
  191. >        of the pointer returned  by  new[]...
  192. >        ...the expression passed to `delete []' must be a
  193. >        pointer to the first element of an array of objects
  194. >        allocated with `new []'...
  195.  
  196. It does say that the dynamic type of the pointer passed to delete[] must
  197. match its static type, which is effectively the same thing.
  198. (See 5.3.5/3.)
  199.  
  200. >    And here we really arrive to the final point. ANSI draft gives
  201. >no   strong   array   definition   to   disable  ambiguous   array
  202. >interpretation.  And    above-mentioned  common-sense based  array
  203. >understanding has all rights to exist.
  204.  
  205. The draft is definitely not easy reading, but I think it does define
  206. things sufficiently well to allow unambiguous interpretation in this
  207. case.
  208.  
  209. >    Addition to array definition [dcl.array]:
  210. >        Array of N T object represents contiguous amount of
  211. >        memory  of  suitable  size  and  alignment  with  N
  212. >        non-overlapping objects  of type T  placed into the
  213. >        memory with no gaps and each properly aligned.
  214.  
  215. I think this is already covered by the wording on array lifetimes (3.8)
  216. and the statement in [dcl.array] that an array object consists of
  217. contiguously allocated elements.  But adding that wording might make
  218. things clearer.
  219.  
  220. >    Addition to operator delete [expr.delete] ("above" here
  221. >    means all previously said by the standard):
  222. >        In either alternative, the type of the deleted object
  223. >        is evaluated  as described above and according to the
  224. >        type of the actual operand.
  225.  
  226. The draft says that if the dynamic type is different from the static
  227. type, then the behaviour is undefined.  So the above text would be
  228. a change, not just a clarification.  I'm not at all convinced that
  229. it would be a change for the better.  Leaving the behaviour
  230. undefined in this case gives implementors more flexibility, and
  231. I think that flexibility is probably more important than defining
  232. the behaviour of programs like yours which play tricks with memory
  233. allocation.
  234.  
  235. --
  236. Fergus Henderson                 WWW: http://www.cs.mu.oz.au/~fjh
  237. fjh@cs.mu.oz.au                  PGP: finger fjh@128.250.37.3
  238. ---
  239. [ To submit articles: try just posting with your news-reader.
  240.                       If that fails, use mailto:std-c++@ncar.ucar.edu
  241.   FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html
  242.   Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html
  243.   Comments? mailto:std-c++-request@ncar.ucar.edu.
  244. ]
  245.